package it.fuesi.synoutils.services.client;

import android.util.Log;

import com.squareup.okhttp.Connection;
import com.squareup.okhttp.OkHttpClient;

import org.apache.http.client.HttpClient;
import org.apache.http.impl.DefaultHttpClientConnection;

import java.net.CookieHandler;
import java.net.CookieManager;
import java.net.CookiePolicy;
import java.security.SecureRandom;
import java.security.cert.CertificateException;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSession;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;

import it.fuesi.synoutils.model.config.SynoConfig;
import it.fuesi.synoutils.services.cache.CacheService;
import it.fuesi.synoutils.services.constants.Service;
import it.fuesi.synoutils.services.constants.ServiceType;
import retrofit.RequestInterceptor;
import retrofit.RestAdapter;
import retrofit.android.AndroidLog;
import retrofit.client.OkClient;

/**
 * Created by pascotto on 17/10/14.
 */
public class ServiceClientFactory {

    private static final String TAG = "ServiceClient";

    private static final String FENOPY_ENDPOINT = "http://fenopy.se";
    private static SynoConfig synoConfig;

    private static Map<Service, RestAdapter> adapterInstances = new HashMap<Service, RestAdapter>();

    private static CookieManager cookieManager;

    public static final int CONNECTION_TIMEOUT = 10;
    public static final int READ_TIMEOUT = 30;

    public static final int CONNECTION_TIMEOUT_MILLIS = CONNECTION_TIMEOUT * 1000;
    public static final int READ_TIMEOUT_MILLIS = READ_TIMEOUT * 1000;

    public static CookieManager getCookieManager(){
        return cookieManager;
    }

    public static void setSynoConfig(SynoConfig config){
        ServiceClientFactory.synoConfig = config;
        CacheService.setSid(null);
        adapterInstances.clear();
    }

    public static SynoConfig getSynoConfig(){
        return synoConfig;
    }


    public static String getEndpoint(SynoConfig config){
        return config.getProtocol() + "://" + config.getHost() + ":" + config.getPort();
    }

    private static SSLSocketFactory sslSocketFactory;

    public static SSLSocketFactory getSSLSocketFactory(){
        if(sslSocketFactory != null)
            return sslSocketFactory;

        // Imports: javax.net.ssl.TrustManager, javax.net.ssl.X509TrustManager
        try {
            // Create a trust manager that does not validate certificate chains
            final TrustManager[] trustAllCerts = new TrustManager[] { new X509TrustManager() {


                @Override
                public void checkClientTrusted(java.security.cert.X509Certificate[] x509Certificates, String s) throws CertificateException {

                }

                @Override
                public void checkServerTrusted(java.security.cert.X509Certificate[] x509Certificates, String s) throws CertificateException {

                }

                @Override
                public java.security.cert.X509Certificate[] getAcceptedIssuers() {
                    return new java.security.cert.X509Certificate[0];
                }


            } };

            // Install the all-trusting trust manager
            final SSLContext sslContext = SSLContext.getInstance( "SSL" );
            sslContext.init( null, trustAllCerts, new java.security.SecureRandom() );
            // Create an ssl socket factory with our all-trusting manager
            sslSocketFactory = sslContext.getSocketFactory();

            System.setProperty( "sun.security.ssl.allowUnsafeRenegotiation", "true" );

            HttpsURLConnection.setDefaultHostnameVerifier(new NullHostNameVerifier());
            SSLContext context = SSLContext.getInstance("TLS");
            context.init(null, new X509TrustManager[]{new NullX509TrustManager()}, new SecureRandom());
            HttpsURLConnection.setDefaultSSLSocketFactory(context.getSocketFactory());

            SSLContext.setDefault(context);


            cookieManager = new CookieManager();
            cookieManager.setCookiePolicy(CookiePolicy.ACCEPT_ALL);
            CookieHandler.setDefault(cookieManager);


        } catch ( final Exception e ) {
            e.printStackTrace();
            sslSocketFactory = null;
        }

        return sslSocketFactory;
    }

    public static RestAdapter getRestAdapter(Service service) throws IllegalArgumentException{
        if(adapterInstances.containsKey(service)) return adapterInstances.get(service);

        final String endPoint;

        if(service == Service.Synology) {
            if (synoConfig == null)
                throw new IllegalArgumentException("Synology configuration not set");

            endPoint = getEndpoint(synoConfig);
        }else if(service == Service.Fenopy){
            endPoint = FENOPY_ENDPOINT;
        }else
            endPoint = null;



        OkHttpClient httpClient = new OkHttpClient();
        httpClient.setSslSocketFactory(getSSLSocketFactory());
        httpClient.setHostnameVerifier(new NullHostNameVerifier());
        httpClient.setConnectTimeout(CONNECTION_TIMEOUT, TimeUnit.SECONDS);
        httpClient.setReadTimeout(READ_TIMEOUT, TimeUnit.SECONDS);
        httpClient.setCookieHandler(CookieHandler.getDefault());


        adapterInstances.put(service, new RestAdapter.Builder()
                .setClient(new OkClient(httpClient))
                .setLogLevel(RestAdapter.LogLevel.NONE).setLog(new AndroidLog(TAG))
                .setEndpoint(endPoint)
                .build());



        return adapterInstances.get(service);
    }

    public static <T> T getClient(Class<T> clientClass) throws IllegalArgumentException{
        ServiceType type = clientClass.getAnnotation(ServiceType.class);

        Log.d(TAG, "ServiceType: " + type);
        final RestAdapter restAdapter = getRestAdapter(type.value());

        return restAdapter.create(clientClass);
    }


    private static class NullHostNameVerifier implements HostnameVerifier {

        public boolean verify(String hostname, SSLSession session) {
            Log.i("RestUtilImpl", "Approving certificate for " + hostname);
            return true;
        }
    }

    private static class NullX509TrustManager implements X509TrustManager{

        @Override
        public void checkServerTrusted(java.security.cert.X509Certificate[] x509Certificates, String s) throws CertificateException {

        }

        @Override
        public void checkClientTrusted(java.security.cert.X509Certificate[] x509Certificates, String s) throws CertificateException {

        }

        @Override
        public java.security.cert.X509Certificate[] getAcceptedIssuers() {
            return new java.security.cert.X509Certificate[0];
        }
    }



}
